﻿using System;
using System.Collections;
using System.Reflection;
using System.Text;
using Demo.History.Entities;
using VitCode.Castle.DataAccess;
using VitCode.Castle.Entities;
using VitCode.Castle.Helper;
using VitCode.ObjectResolutionHelper;


namespace AMS.History.DataAccess
{
    /// <summary>
    /// 历史项目的数据访问器
    /// 用于实现历史项目数据访问的特定业务
    /// </summary>
    public class HistoryAccess : BaseDataAccess
    {
        private readonly ObjectBuildHelper m_ObjBuilderHlp = new ObjectBuildHelper();

        private void SetNeedSaveMoObject(ref ArrayList alNeedSaveMoObjects, object newHisObj,object baseObj)
        {
            if (newHisObj == null || baseObj == null)
                return;
            if (baseObj is BaseEntity && newHisObj is BaseHistoryEntity)
            {
                ((BaseEntity)baseObj).CurrHisId = ((BaseHistoryEntity)newHisObj).HId;
                alNeedSaveMoObjects.Add(baseObj);
            }
            
        }

        public void SaveMonitorObjs(MonitorObjects moObjects, Type objType, Type hisObjType, string objKeyProName,
                                    HisVersionInfo verInfo)
        {
            var nodSaveObjects = new NameObjectDictionary();
            var nodCompareHisObjects = new NameObjectDictionary();
            var alNeedSaveMoObjects = new ArrayList();

            if (moObjects == null)
                return;
            IList lstObjects = moObjects.GetListenObjects();
            if (lstObjects == null || lstObjects.Count < 1)
                return;
            foreach (object obj in lstObjects)
            {
                if (obj == null || obj.GetType() != objType)
                    continue;
                PropertyInfo objKeyPro = obj.GetType().GetProperty(objKeyProName);
                if (objKeyPro != null)
                {
                    object objKeyVal = objKeyPro.GetValue(obj, null);
                    if (objKeyVal == null)
                        continue;
                    string strKeyVal = objKeyVal.ToString();
                    nodSaveObjects[strKeyVal] = obj;
                }
            }

            if (nodSaveObjects.Count > 0)
            {
                string hqlHeadString = "FROM " + hisObjType.Name + " o WHERE o.HIsLast=1 and (";
                var sb = new StringBuilder();
                bool isFirst = true;
                for (int i = 0; i < nodSaveObjects.Keys.Count; i++)
                {
                    string strKeyVal = nodSaveObjects.Keys[i];
                    if (!isFirst)
                        sb.Append(" or ");
                    else
                        isFirst = false;
                    sb.Append(" " + HistoryKeyInfo.H_BASE_OBJECT_ID + "='" + strKeyVal + "' ");


                    if (i%100 == 0 || i == (nodSaveObjects.Count - 1))
                    {
                        IList lstLastHisObjs = Session.CreateQuery(hqlHeadString + sb + " ) ").List();
                        isFirst = true;
                        sb = new StringBuilder();
                        if (lstLastHisObjs != null && lstLastHisObjs.Count > 0)
                        {
                            foreach (object hisObj in lstLastHisObjs)
                            {
                                PropertyInfo pi = hisObjType.GetProperty(HistoryKeyInfo.H_BASE_OBJECT_ID);
                                if (pi == null)
                                    throw new ArgumentException("历史对象没有属性\"" + HistoryKeyInfo.H_BASE_OBJECT_ID + "\"");
                                object objHisKey = pi.GetValue(hisObj, null);
                                if (objHisKey == null)
                                    continue;
                                string strHisKey = objHisKey.ToString();
                                nodCompareHisObjects[strHisKey] = hisObj;
                            }
                        }
                    }
                }
            }

            var nodSaveObjHisRecords = new NameObjectDictionary();
            foreach (string saveObjKey in nodSaveObjects.Keys)
            {
                object obj = nodSaveObjects[saveObjKey];
                if (nodCompareHisObjects.Contains(saveObjKey))
                {
                    object hisObj = nodCompareHisObjects[saveObjKey];
                    bool compareResult = BaseHistoryEntity.CompareToBaseHisObject(obj, hisObj,verInfo);
                    if (!compareResult)
                    {
                        SaveOrUpdate(verInfo);
                        object newHisObj = Activator.CreateInstance(hisObjType);
                        new ObjectBuildHelper().InvokeObjMethod(newHisObj, HistoryKeyInfo.CREATE_HIS_RECORD,
                                                                new[] {obj, verInfo.Id, ""});
                        SetPevCurHisRecordInfo(hisObj, newHisObj);
                        
                        nodSaveObjHisRecords[saveObjKey + "_Pev"] = hisObj;
                        nodSaveObjHisRecords[saveObjKey] = newHisObj;
                        alNeedSaveMoObjects.Add(obj);
                    }
                }
                else
                {
                    SaveOrUpdate(verInfo);
                    object newHisObj = Activator.CreateInstance(hisObjType);
                    new ObjectBuildHelper().InvokeObjMethod(newHisObj, HistoryKeyInfo.CREATE_HIS_RECORD,
                                                            new[] {obj, verInfo.Id, ""});
                    nodSaveObjHisRecords[saveObjKey] = newHisObj;
                    alNeedSaveMoObjects.Add(obj);
                }
            }

            if (nodSaveObjHisRecords.Count > 0)
                SaveOrUpdate(verInfo);

            foreach (object needToSaveHisObj in nodSaveObjHisRecords.Values)
            {
                SaveOrUpdate(needToSaveHisObj);
            }

            foreach (var obj in alNeedSaveMoObjects)
            {
                if(obj is BaseEntity)
                {
                    var strId = ((BaseEntity) obj).Id.ToString();
                    if(nodSaveObjHisRecords.Contains(strId))
                    {
                        var hisObj = nodSaveObjHisRecords[strId];
                        if(hisObj is BaseHistoryEntity)
                        {
                            ((BaseEntity) obj).CurrHisId = ((BaseHistoryEntity) hisObj).HId;
                        }
                    }
                }
                SaveOrUpdate(obj);
            }
        }

        public void SaveHisRecord(object obj,Type hisObjType, string strKeyField, string strHisMemo)
        {
            if (obj == null || hisObjType == null || strHisMemo == null || strKeyField.Length == 0)
                return;
            var moObjs = new MonitorObjects();
            var hisVersion = new HisVersionInfo();
            hisVersion.Memo = strHisMemo;
            hisVersion.OperatorUserId = GenericContext.CurrUserId;
            moObjs.Listen(obj);
            SaveMonitorObjs(moObjs, obj.GetType(), hisObjType, strKeyField, hisVersion);
        }

        private void SetPevCurHisRecordInfo(object pevHisObj, object curHisObj)
        {
            m_ObjBuilderHlp.SetProperty(pevHisObj, HistoryKeyInfo.H_IS_LAST, false);
            object perHisId = m_ObjBuilderHlp.GetPropertyValue(HistoryKeyInfo.H_ID, pevHisObj);
            m_ObjBuilderHlp.SetProperty(curHisObj, HistoryKeyInfo.H_PARENT_ID, perHisId);
        }
    }
}